Add admin-editable alternative search terms per mod, searchable on /admin/ranked-mods - #52
Merged
Merged
Conversation
…branch tip skyline69/balatro-mod-index's own update_mod_versions.py bumps a branch-tracked mod's `version` field to the latest commit SHA of the *whole repo* on any commit anywhere in it, but only ever rewrites `downloadURL` for its tag/release cases -- never for the HEAD case. So every version ever recorded for such a mod carries the exact same branch-archive URL, and downloading it always fetches "whatever's on the branch right now", never the specific commit the version label names. Confirmed live: Aikoyori@Aikoyoris-Shenanigans' version history has a dozen distinct commit-hash labels all sharing one identical downloadUrl and (whenever the branch hadn't moved between two label bumps) identical sha256. The real cost isn't the duplication -- it's that an older label becomes permanently unfetchable once the branch advances past it, silently breaking any profile (a Ranked rankedVersion pin, or a user manually pinning an older dropdown entry) sitting on it. Add resolveCommitPinnedDownloadUrl() (custom-mod-version-check.service.ts, alongside its existing GitHub-resolution helpers): when a version looks like a git SHA and its downloadUrl classifies as 'branch', resolve the SHA to its full 40-char form via one GitHub API call and rebuild a codeload.github.com/.../zip/<full-sha> URL that stays fetchable forever, regardless of where the branch moves next. Wire it into mods-sync.service.ts via pinBranchVersionIfNew(), called right before a (modId, version) pair is hashed and stored for the first time -- both for upstream-index entries and for automaticVersionCheck custom mods tracking a branch. A version that's already been hashed is left alone unconditionally, so this costs one extra GitHub API call per brand-new branch-tracked version, never per sync. Known follow-up (not done here): existing mod_registry_versions rows synced before this fix keep their stale branch URL -- only new versions get pinned going forward. A one-off backfill (mirroring recomputeAllModHashes's shape) would be needed to retroactively pin already-stored branch-tracked versions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
pinBranchVersionIfNew() (previous commit) only pins a version's downloadUrl the first time it's synced -- every mod_registry_versions row written before that fix exists is still sitting on its original moving-branch-tip URL, with a sha256 computed against whatever that tip happened to be at hash-time (not necessarily the exact commit its own version label names). Add backfill-branch-pins.ts (pnpm backfill-branch-pins), a one-off script in the same shape as the existing backfill-mod-hashes.ts: finds every mod_registry_versions row whose downloadUrl still classifies as 'branch', resolves a commit-pinned URL for it, and *re-downloads and re-hashes* it against that pinned URL rather than trusting the existing stored hash -- the old hash could already be wrong for the commit it's about to be pinned to, so only a fresh hash guarantees correctness. Each row gets up to 3 resolve+hash attempts with a short backoff before being marked via a new mod_registry_versions.pin_failed_at column (migration 0036) and skipped on future runs -- so a genuinely dead repo/branch/commit doesn't keep burning GitHub API calls on every re-run. Pass --retry-failed to re-attempt previously-marked rows (e.g. after a renamed repo or an expired rate limit). New gateway functions in mods.gateway.ts: listVersionsWithDownloadUrl (broad fetch, filtered/classified in TS same as listAllVersionsWithDownloadUrl), applyBranchPin (writes the pinned URL + hash, clears pinFailedAt), markVersionPinFailed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…dmin/ranked-mods Admins managing the ranked mod catalog had no way to find a mod by anything other than its real title/id - e.g. "What's in my Fool" is commonly called "wimf" by players, but typing that into a (previously nonexistent) search box would have found nothing. - mod_registry gets a new search_terms text[] column (migration 0036_mod_search_terms.sql), same shape as the existing categories column but with no upstream-index counterpart at all: unlike categories/title/author/etc (SYNCABLE_MOD_FIELDS), this is never touched by the hourly upstream sync and never participates in overriddenFields - there's no upstream value for it to ever protect. updateModFields() writes it directly instead of through the touch()/overriddenFields path the ten real syncable fields use. - Editable via the same admin field-edit form as categories (PATCH /webadmin/mods/:modId, and at creation via POST /webadmin/mods / PUT .../custom) - a comma-separated "Alternative search terms" input right below Categories in the mod dialog. - /admin/ranked-mods now has an actual search box (there wasn't one at all before) that filters the already-loaded catalog client-side by name, id, or any searchTerms entry, case-insensitive substring on each - the catalog is small enough this needs no server round trip per keystroke. The table's empty-state message now distinguishes "no mods synced yet" from "no mods match this search". Verified: tsc --noEmit on both apps/server and apps/web shows no errors introduced by this change (every remaining error is a pre-existing missing-module gap in this checkout, e.g. @bmp/types/sanitize-html not installed, unrelated to any file this touches). biome check flagged only pre-existing formatting/line-ending debt already present on these files before this change (confirmed via git stash comparison), nothing new. Not yet run against a live database - the migration should be applied (pnpm migrate) before this deploys.
V-rtualized
force-pushed
the
feat/mod-alternative-search-terms
branch
from
September 13, 2026 20:16
7364101 to
1cfeb78
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Admins managing the ranked mod catalog had no way to find a mod by anything other than its real title/id - e.g. "What's in my Fool" is commonly called "wimf" by players, but there was no way to associate that alias with the mod, and no search box on
/admin/ranked-modsat all.Changes
mod_registrygets a newsearch_terms text[]column (migration0036_mod_search_terms.sql), same shape as the existingcategoriescolumn but with no upstream-index counterpart at all: unlikecategories/title/author/etc (SYNCABLE_MOD_FIELDS), this is never touched by the hourly upstream sync and never participates inoverriddenFields- there's no upstream value for it to ever protect.updateModFields()writes it directly instead of through thetouch()/overriddenFieldspath the ten real syncable fields use.PATCH /webadmin/mods/:modId, and at creation viaPOST /webadmin/mods/PUT .../custom) - a comma-separated "Alternative search terms" input right below Categories in the mod dialog./admin/ranked-modsnow has an actual search box (there wasn't one at all before) that filters the already-loaded catalog client-side by name, id, or anysearchTermsentry, case-insensitive substring on each - the catalog is small enough this needs no server round trip per keystroke. The table's empty-state message now distinguishes "no mods synced yet" from "no mods match this search".Verification
tsc --noEmiton bothapps/serverandapps/webshows no errors introduced by this change (every remaining error is a pre-existing missing-module gap in this checkout, e.g.@bmp/types/sanitize-htmlnot installed, unrelated to any file this touches).biome checkflagged only pre-existing formatting/line-ending debt already present on these files before this change (confirmed viagit stashcomparison), nothing new.pnpm migrate) before this deploys.Follow-up
This only covers the admin website. The launcher (BET) doesn't yet consume
searchTermsfor its own in-app mod search - separate PR if wanted.🤖 Generated with Claude Code